/*
  全局事件总线&自定义事件的原理：
    $on(eventName, callback)    绑定持久事件
    $once(eventName, callback)  绑定一次性事件
    $off(eventName, callback)  解绑事件的某个回调
      $off(eventName)  解绑事件的所有回调
      $off()  解绑所有事件和所有回调
    $emit(eventName[, ...args]) 触发事件
*/

class EventBus {
  constructor() {
    // 初始化存储事件的容器
    this._events = {};
  }
  /**
   * 绑定事件的方法
   * @param {String} eventName 事件名称
   * @param {Function} callback 事件回调函数
   */
  on(eventName, callback) {
    /* 
      将事件和回调函数存储起来 --> 为了触发事件时可以找到并触发
      存储事件的容器：events
        {
          eventName: [callback1, callback2, callback3...]
        }
    */
    if (this._events[eventName]) {
      // 有值：之前保存callback数据，已经是一个数组了
      this._events[eventName].push(callback);
      return;
    }
    // 没有值，之前没有保存这个事件
    this._events[eventName] = [callback];
  }

  /**
   * 绑定一次性事件
   * @param {String} eventName 事件名称
   * @param {Function} callback 事件回调函数
   */
  once(eventName, callback) {
    // 触发一次事件，会自动解绑事件
    const cb = (...args) => {
      callback(...args);
      this.off(eventName, cb);
    }

    this.on(eventName, cb);
  }

  /**
   * 解绑事件
   * @param {String} eventName 事件名称
   * @param {Function} callback 事件回调函数
   */
  off(eventName, callback) {
    // 去容器中找到事件的回调，干掉其中一个
    if (!this._events[eventName]) return;
    this._events[eventName] = this._events[eventName].filter(cb => cb !== callback)
  }

  /**
   * 触发事件
   * @param {String} eventName 事件名称
   * @param  {...any} args 事件参数
   */
  emit(eventName, ...args) {
    // 从容器中找到相应事件的回调函数，并调用
    // 调用时要传入相应的参数
    if (!this._events[eventName]) return;
    this._events[eventName].forEach((cb) => cb(...args));
  }
}

const bus = new EventBus();

bus.on('aaa', function (x) {
  console.log(1111, x);
})

const cb = function (x) {
  console.log(2222, x);
}
bus.on('aaa', cb)
bus.once('aaa', function (x) {
  console.log(3333, x);
})

bus.emit('aaa', 123)

bus.off('aaa', cb)

bus.emit('aaa', 456)